C99にクラスはありませんが、構造体と共用体ならあります。また、C++(C++98)と同様、列挙体も使うことができます。
タグ名だけで型名にならない
C++では、クラスや列挙体のタグ名だけで型名になりましたが、C99では、明示的に struct, union, または enum を付けなければなりません。それが嫌なら、typedef 名を定義する必要があります。
0 1 2 3 4 5 |
struct A { int a; }; A x; /* エラー */ struct A x; /* OK */ |
クラス有効範囲のようなものはない
C++では、クラスの中でクラスを定義した場合、入れ子になったクラスは、外側のクラスのクラス有効範囲に属します。しかし、C99ではクラス有効範囲という概念がないため、このようなことはありません。
0 1 2 3 4 5 6 7 8 9 10 |
struct A { struct B { int value; } b; } a; struct B x; /* OK */ |
構造体や共用体の中で型定義はできない
C99にはクラス有効範囲がありませんので、構造体や共用体の中で型定義を行うことができません。
0 1 2 3 4 5 6 7 8 9 |
struct A { typedef int int_t; /* エラー */ struct B { int b; }; /* エラー */ enum { c, d }; /* エラー */ int a; }; |
集成体
C99では、「集成体」という用語は構造体と配列の総称です。共用体は集成体ではありません
構造体のメモリ配置
構造体型のオブジェクトへのポインタは、そのオブジェクトの最初のメンバへのポインタと同じアドレスをさします。また、構造体のメンバは上から順に、メモリの下位→上位の順に配置されます。
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
#include <assert.h> struct A { int a; double b; }; int main(void) { struct A x; assert(&x == (struct A*)&x.a); /* OK */ return 0; } |
構造体や共用体のメンバは必須
C99では、メンバのない構造体や共用体を定義することはできません。
フレキシブル配列メンバ
C99では、二つ以上の名前付きメンバを持つ構造体の最後のメンバとして、フレキシブル配列メンバを使うことができます。フレキシブル配列メンバというのは、不完全配列型のメンバのことです。
0 1 2 3 4 5 6 |
struct foo { int a; char b[]; }; |
フレキシブル配列メンバを使えば、構造体の末尾に可変長の配列をつなげることが容易にできるようになります。
0 1 2 3 4 |
char str[] = "This is a pen."; struct foo *ptr = malloc(sizeof(struct foo) + sizeof str); strcpy(ptr->b, str); |
ビットフィールド
C99では、ビットフィールドの型として使えるのは、_Bool, int, signed int, およびunsigned intだけです。signedもunsignedも付かない単なるintの場合、符号付きか符号無しかは、C++と同様、処理系定義です。
無名共用体は使えない
C99では、無名共用体をメンバ名だけでアクセスすることはできません。
0 1 2 3 4 5 6 7 8 9 10 11 |
int main(void) { union { int a; char b[10]; }; a = 123; /* エラー */ return 0; } |
列挙定数はint型
C99は、列挙定数は常にint型になります。列挙体型は処理系定義の汎整数型になります。
列挙子並び末尾のコンマ
C99では、列挙子並びの末尾にコンマを付けることができます。
0 1 2 3 4 5 6 7 |
enum foo { abc, def, ghi , }; |
式中での型定義
C99では、式や関数の仮引数並びで型定義を行うことができます。具体的には、次のようなことができます。
0 1 2 |
a = ((struct { int a; double b; }*)ptr)->b; |